QuickOPC User's Guide and Reference
Installed Examples - WindowsForms - OvenControl

Monitors sensors in an industrial oven, indicates level alarms by changing colors, allows the user to change a setpoint, and logs the values into a CSV file.

The form:

// $Header: $
// Copyright (c) CODE Consulting and Development, s.r.o., Plzen. All rights reserved.

// ReSharper disable CommentTypo

using System;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;
using JetBrains.Annotations;
using OpcLabs.EasyOpc;
using OpcLabs.EasyOpc.DataAccess;
using OpcLabs.EasyOpc.DataAccess.OperationModel;
using OpcLabs.EasyOpc.OperationModel;

namespace OvenControl
{
    public partial class Form1 : Form
    {
        private const string MachineName = "";
        private const string ServerClass = "SWToolbox.TOPServer.V5";    // or "Kepware.KEPServerEX.V5"

        private DAVtq _fanPower;
        private DAVtq _heaterPower;
        private DAVtq _ovenTemperature;
        private DAVtq _heaterTemperature;
        private DAVtq _fanSpeed;
        private DAVtq _temperatureSetpoint;


        // ReSharper disable once NotNullMemberIsNotInitialized
        public Form1()
        {
            InitializeComponent();
        }

        private Color _defaultBackColor = Color.White;

        
        // ReSharper disable InconsistentNaming
        private void Form1_Load(object sender, EventArgs e)
        // ReSharper restore InconsistentNaming
        {
            SetDefaults();
            _defaultBackColor = txbOvenTemperature.BackColor;
        }

        private void SetDefaults()
        {
            nudUpdateRate.Value = 10;
        }

        // ReSharper disable InconsistentNaming
        private void btnStart_Click(object sender, EventArgs e)
        // ReSharper restore InconsistentNaming
        {
            btnStart.Enabled = false;
            timer1.Interval = Decimal.ToInt32(nudUpdateRate.Value * 1000);
            timer1.Start();
            UpdateValuesAndLog();
            btnStop.Enabled = true;
        }

        // ReSharper disable InconsistentNaming
        private void btnStop_Click(object sender, EventArgs e)
        // ReSharper restore InconsistentNaming
        {
            btnStop.Enabled = false;
            timer1.Stop();
            btnStart.Enabled = true;
        }

        // ReSharper disable InconsistentNaming
        private void timer1_Tick(object sender, EventArgs e)
        // ReSharper restore InconsistentNaming
        {
            UpdateValuesAndLog();
        }

        private static Int32? ConvertValueToInt32(DAVtq vtq)
        {
            if (vtq is null) return null;
            if (!vtq.HasValue) return null;
            return vtq.Value as Int32?;
        }

        private static string GetTextVtq(DAVtq vtq)
        {
            if ((vtq is null) || (!vtq.HasValue))
            {
                return "???";
            }
            return vtq.DisplayValue();
        }

        private void DisplayVtq(
            DAVtq vtq, 
            // ReSharper disable once SuggestBaseTypeForParameter
            TextBox txb, 
            int colorTestId)
        {
            if ((vtq is null) || (!vtq.HasValue))
            {
                // ReSharper disable LocalizableElement
                txb.Text = "???";
                // ReSharper restore LocalizableElement
                txb.BackColor = Color.Magenta;
            }
            else
            {
                txb.Text = vtq.DisplayValue();

                Int32? value;
                Color backColor = _defaultBackColor;
                switch (colorTestId)
                {
                    case 0: 
                        txb.BackColor = _defaultBackColor; 
                        break;
                
                    case 1: // oven temperature
                        value = ConvertValueToInt32(vtq);
                        if (value.HasValue)
                        {
                            Int32? range = ConvertValueToInt32(_temperatureSetpoint);
                            if (range.HasValue)
                            {
                                if (value <= (range - 5)) backColor = Color.Blue;
                                else
                                    if (value >= (range + 5)) backColor = Color.Red;
                            }
                        }
                        txb.BackColor = backColor;
                        break;

                    case 2: // oven temperature
                        value = ConvertValueToInt32(vtq);
                        // ReSharper disable once UseNullPropagation
                        if (value.HasValue)
                        {
                            if (value < 200) backColor = Color.Red;
                        }
                        txb.BackColor = backColor;
                        break;
                }
            }
        }

        private DAVtq ReadVtq(string itemId)
        {
            DAVtq vtq;
            Exception exception = null;
            try
            {
                vtq = easyDAClient1.ReadItem(MachineName, ServerClass, itemId);
            }
            catch (OpcException ex)
            {
                exception = ex;
                vtq = null;
            }
            DisplayException(exception);
            return vtq;
        }

        private DAVtqResult[] ReadMultipleVtq(string[] itemIds)
        {
            DAVtqResult[] results;
            Exception exception = null;
            var itemDescriptors = new DAItemDescriptor[itemIds.Length];
            for (int i = 0; i < itemIds.Length; i++)
            {
                Debug.Assert(itemIds[i] != null);
                itemDescriptors[i] = new DAItemDescriptor(itemIds[i]);
            }
            try
            {
                results = easyDAClient1.ReadMultipleItems(new ServerDescriptor(MachineName, ServerClass), 
                    itemDescriptors);
            }
            catch (OpcException ex)
            {
                exception = ex;
                results = null;
            }
            DisplayException(exception);
            return results;
        }

        private string _lastExceptionMessage = "";
        private void DisplayException(Exception exception)
        {
            //txbExceptions.Text = (exception is null) ? "" : exception.GetBaseException().Message;
            if (!(exception is null))
            {
                string newMessage = $"{DateTime.Now} {exception.GetBaseException().Message}";
                if (_lastExceptionMessage != newMessage)
                {
                    _lastExceptionMessage = newMessage;
                    txbExceptions.AppendText(newMessage + Environment.NewLine);
                }
            }
        }

        private void ReadMultiple()
        {
            DAVtqResult[] results = ReadMultipleVtq(new[]
               {
                   "Channel1.Device1.FanPower", "Channel1.Device1.FanSpeed", "Channel1.Device1.HeaterPower", 
                   "Channel1.Device1.HeaterTemp", "Channel1.Device1.TempSetPoint", "Channel1.Device1.OvenTemp"
               });

            if (results is null)
                return;

            Debug.Assert(results[0] != null);
            Debug.Assert(results[1] != null);
            Debug.Assert(results[2] != null);
            Debug.Assert(results[3] != null);
            Debug.Assert(results[4] != null);
            Debug.Assert(results[5] != null);

            if (results[0].Exception is null) _fanPower = results[0].Vtq;
            else
            {
                _fanPower = null;
                DisplayException(results[0].Exception);
            }
            if (results[1].Exception is null) _fanSpeed = results[1].Vtq;
            else
            {
                _fanSpeed = null;
                DisplayException(results[1].Exception);
            }
            if (results[2].Exception is null) _heaterPower = results[2].Vtq;
            else
            {
                _heaterPower = null;
                DisplayException(results[2].Exception);
            }
            if (results[3].Exception is null) _heaterTemperature = results[3].Vtq;
            else
            {
                _heaterTemperature = null;
                DisplayException(results[3].Exception);
            }
            if (results[4].Exception is null) _temperatureSetpoint = results[4].Vtq;
            else
            {
                _temperatureSetpoint = null;
                DisplayException(results[4].Exception);
            }
            if (results[5].Exception is null) _ovenTemperature = results[5].Vtq;
            else
            {
                _ovenTemperature = null;
                DisplayException(results[5].Exception);
            }
        }


        private void UpdateValuesAndLog()
        {
            // read multiple item
            ReadMultiple();

            DisplayVtq(_fanPower, txbFanPower, 0);
            DisplayVtq(_fanSpeed, txbFanSpeed, 0);
            DisplayVtq(_heaterPower, txbHeaterPower, 0);
            DisplayVtq(_heaterTemperature, txbHeaterTemperature, 2);
            DisplayVtq(_temperatureSetpoint, txbTemperatureSetpoint, 0);
            DisplayVtq(_ovenTemperature, txbOvenTemperature, 1);

            WriteToLog();
        }

        // ReSharper disable InconsistentNaming
        private void btnSetTemperatureSetpoint_Click(object sender, EventArgs e)
        // ReSharper restore InconsistentNaming
        {
            object value = txbNewTemperatureSetpoint.Text;
            Exception exception = null;
            try
            {
                easyDAClient1.WriteItemValue(
                    MachineName,
                    ServerClass,
                    "Channel1.Device1.TempSetPoint",
                    value);
            }
            catch (OpcException ex)
            {
                exception = ex;
            }
            DisplayException(exception);

            _temperatureSetpoint = ReadVtq("Channel1.Device1.TempSetPoint");
            DisplayVtq(_temperatureSetpoint, txbTemperatureSetpoint, 0);
            DisplayVtq(_ovenTemperature, txbOvenTemperature, 1);

        }

        private void WriteToLog()
        {
            try
            {
                var sw = new System.IO.StreamWriter(Application.StartupPath + "\\OvenControl.csv", true);
                sw.WriteLine(
                    DateTime.Now.ToString(CultureInfo.InvariantCulture) + "," +  
                    GetTextVtq(_fanPower) + "," +  
                    GetTextVtq(_heaterPower) + "," +  
                    GetTextVtq(_ovenTemperature) + "," +  
                    GetTextVtq(_heaterTemperature) + "," +  
                    GetTextVtq(_fanSpeed) + "," +  
                    GetTextVtq(_temperatureSetpoint));

                sw.Close();
            }
            catch (Exception e)
            {
                DisplayException(e);
            }
        }

        // ReSharper disable InconsistentNaming
        private void btnClose_Click(object sender, EventArgs e)
        // ReSharper restore InconsistentNaming
        {
            Close();
        }
    }
}
Imports System.Globalization
Imports JetBrains.Annotations
Imports OpcLabs.EasyOpc
Imports OpcLabs.EasyOpc.DataAccess
Imports OpcLabs.EasyOpc.OperationModel
Imports OpcLabs.EasyOpc.DataAccess.OperationModel


Partial Public Class Form1
    Inherits Form

    Private Const MachineName As String = ""
    Private Const ServerClass As String = "SWToolbox.TOPServer.V5" ' or "Kepware.KEPServerEX.V5"

    Private _fanPower As DAVtq
    Private _heaterPower As DAVtq
    Private _ovenTemperature As DAVtq
    Private _heaterTemperature As DAVtq
    Private _fanSpeed As DAVtq
    Private _temperatureSetpoint As DAVtq


    Public Sub New()
        InitializeComponent()
    End Sub

    Private _defaultBackColor As Color = Color.White


    ' ReSharper disable InconsistentNaming
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
        ' ReSharper restore InconsistentNaming
        SetDefaults()
        _defaultBackColor = txbOvenTemperature.BackColor
    End Sub

    Private Sub SetDefaults()
        nudUpdateRate.Value = 10
    End Sub

    ' ReSharper disable InconsistentNaming
    Private Sub btnStart_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnStart.Click
        ' ReSharper restore InconsistentNaming
        btnStart.Enabled = False
        timer1.Interval = Decimal.ToInt32(nudUpdateRate.Value * 1000)
        timer1.Start()
        UpdateValuesAndLog()
        btnStop.Enabled = True
    End Sub

    ' ReSharper disable InconsistentNaming
    Private Sub btnStop_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnStop.Click
        ' ReSharper restore InconsistentNaming
        btnStop.Enabled = False
        timer1.Stop()
        btnStart.Enabled = True
    End Sub

    ' ReSharper disable InconsistentNaming
    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles timer1.Tick
        ' ReSharper restore InconsistentNaming
        UpdateValuesAndLog()
    End Sub

    Private Function ConvertValueToInt32(ByVal vtq As DAVtq) As Int32?
        If vtq Is Nothing Then
            Return Nothing
        End If
        If Not vtq.HasValue() Then
            Return Nothing
        End If
        Return CType(vtq.Value, Int32?)
    End Function

    Private Function GetTextVtq(ByVal vtq As DAVtq) As String
        If (vtq Is Nothing) OrElse ((Not vtq.HasValue())) Then
            Return "???"
        End If
        Return vtq.DisplayValue()
    End Function

    Private Sub DisplayVtq(ByVal vtq As DAVtq, ByVal txb As TextBox, ByVal colorTestId As Integer)
        If (vtq Is Nothing) OrElse ((Not vtq.HasValue())) Then
            ' ReSharper disable LocalizableElement
            txb.Text = "???"
            ' ReSharper restore LocalizableElement
            txb.BackColor = Color.Magenta
        Else
            txb.Text = vtq.DisplayValue()

            Dim value? As Int32
            Dim aBackColor As Color = _defaultBackColor
            Select Case colorTestId
                Case 0
                    txb.BackColor = _defaultBackColor

                Case 1 ' oven temperature
                    value = ConvertValueToInt32(vtq)
                    If value.HasValue Then
                        Dim range? As Int32 = ConvertValueToInt32(_temperatureSetpoint)
                        If range.HasValue Then
                            If value <= (range - 5) Then
                                aBackColor = Color.Blue
                            Else
                                If value >= (range + 5) Then
                                    aBackColor = Color.Red
                                End If
                            End If
                        End If
                    End If
                    txb.BackColor = aBackColor

                Case 2 ' oven temperature
                    value = ConvertValueToInt32(vtq)
                    If value.HasValue Then
                        If value < 200 Then
                            aBackColor = Color.Red
                        End If
                    End If
                    txb.BackColor = aBackColor
            End Select
        End If
    End Sub

    Private Function ReadVtq(ByVal itemId As String) As DAVtq
        Dim vtq As DAVtq
        Dim exception As Exception = Nothing
        Try
            vtq = easyDAClient1.ReadItem(MachineName, ServerClass, itemId)
        Catch ex As OpcException
            exception = ex
            vtq = Nothing
        End Try
        DisplayException(exception)
        Return vtq
    End Function

    Private Function ReadMultipleVtq(ByVal itemIds() As String) As DAVtqResult()
        Dim results() As DAVtqResult
        Dim exception As Exception = Nothing
        Dim itemDescriptors = New DAItemDescriptor(itemIds.Length - 1) {}
        For i As Integer = 0 To itemIds.Length - 1
            Debug.Assert(itemIds(i) IsNot Nothing)
            itemDescriptors(i) = New DAItemDescriptor(itemIds(i))
        Next i
        Try
            results = easyDAClient1.ReadMultipleItems(New ServerDescriptor(MachineName, ServerClass), itemDescriptors)
        Catch ex As OpcException
            exception = ex
            results = Nothing
        End Try
        DisplayException(exception)
        Return results
    End Function

    Private _lastExceptionMessage As String = ""
    Private Sub DisplayException(ByVal exception As Exception)
        'txbExceptions.Text = exception == null ? "" : exception.GetBaseException().Message;
        If exception IsNot Nothing Then
            Dim newMessage As String = String.Format("{0} {1}", Date.Now, exception.GetBaseException().Message)
            If _lastExceptionMessage <> newMessage Then
                _lastExceptionMessage = newMessage
                txbExceptions.AppendText(newMessage & Environment.NewLine)
            End If
        End If
    End Sub

    Private Sub ReadMultiple()
        Dim results() As DAVtqResult = ReadMultipleVtq(New String() {"Channel1.Device1.FanPower", "Channel1.Device1.FanSpeed", "Channel1.Device1.HeaterPower", "Channel1.Device1.HeaterTemp", "Channel1.Device1.TempSetPoint", "Channel1.Device1.OvenTemp"})

        If results Is Nothing Then
            Return
        End If

        Debug.Assert(results(0) IsNot Nothing)
        Debug.Assert(results(1) IsNot Nothing)
        Debug.Assert(results(2) IsNot Nothing)
        Debug.Assert(results(3) IsNot Nothing)
        Debug.Assert(results(4) IsNot Nothing)
        Debug.Assert(results(5) IsNot Nothing)

        If results(0).Exception Is Nothing Then
            _fanPower = results(0).Vtq
        Else
            _fanPower = Nothing
            DisplayException(results(0).Exception)
        End If
        If results(1).Exception Is Nothing Then
            _fanSpeed = results(1).Vtq
        Else
            _fanSpeed = Nothing
            DisplayException(results(1).Exception)
        End If
        If results(2).Exception Is Nothing Then
            _heaterPower = results(2).Vtq
        Else
            _heaterPower = Nothing
            DisplayException(results(2).Exception)
        End If
        If results(3).Exception Is Nothing Then
            _heaterTemperature = results(3).Vtq
        Else
            _heaterTemperature = Nothing
            DisplayException(results(3).Exception)
        End If
        If results(4).Exception Is Nothing Then
            _temperatureSetpoint = results(4).Vtq
        Else
            _temperatureSetpoint = Nothing
            DisplayException(results(4).Exception)
        End If
        If results(5).Exception Is Nothing Then
            _ovenTemperature = results(5).Vtq
        Else
            _ovenTemperature = Nothing
            DisplayException(results(5).Exception)
        End If
    End Sub


    Private Sub UpdateValuesAndLog()
        ' read multiple item
        ReadMultiple()

        DisplayVtq(_fanPower, txbFanPower, 0)
        DisplayVtq(_fanSpeed, txbFanSpeed, 0)
        DisplayVtq(_heaterPower, txbHeaterPower, 0)
        DisplayVtq(_heaterTemperature, txbHeaterTemperature, 2)
        DisplayVtq(_temperatureSetpoint, txbTemperatureSetpoint, 0)
        DisplayVtq(_ovenTemperature, txbOvenTemperature, 1)

        WriteToLog()
    End Sub

    ' ReSharper disable InconsistentNaming
    Private Sub btnSetTemperatureSetpoint_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSetTemperatureSetpoint.Click
        ' ReSharper restore InconsistentNaming
        Dim value As Object = txbNewTemperatureSetpoint.Text
        Dim exception As Exception = Nothing
        Try
            easyDAClient1.WriteItemValue(MachineName, ServerClass, "Channel1.Device1.TempSetPoint", value)
        Catch ex As OpcException
            exception = ex
        End Try
        DisplayException(exception)

        _temperatureSetpoint = ReadVtq("Channel1.Device1.TempSetPoint")
        DisplayVtq(_temperatureSetpoint, txbTemperatureSetpoint, 0)
        DisplayVtq(_ovenTemperature, txbOvenTemperature, 1)

    End Sub

    Private Sub WriteToLog()
        Try
            Dim sw = New IO.StreamWriter(Application.StartupPath & "\OvenControl.csv", True)
            sw.WriteLine(Date.Now.ToString(CultureInfo.InvariantCulture) & "," & GetTextVtq(_fanPower) & "," & GetTextVtq(_heaterPower) & "," & GetTextVtq(_ovenTemperature) & "," & GetTextVtq(_heaterTemperature) & "," & GetTextVtq(_fanSpeed) & "," & GetTextVtq(_temperatureSetpoint))

            sw.Close()
        Catch e As Exception
            DisplayException(e)
        End Try
    End Sub

    ' ReSharper disable InconsistentNaming
    Private Sub btnClose_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnClose.Click
        ' ReSharper restore InconsistentNaming
        Close()
    End Sub
End Class

 

See Also

Conceptual